/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2012/11/07
* File: EnnMemoryAllocatorConfig.h
* Blog: http://www.cnblogs.com/zengqh/
**/

#ifndef EnnMemoryAllocatorConfig_h__
#define EnnMemoryAllocatorConfig_h__

#include "EnnMemoryAllocatedObject.h"
#include "EnnMemoryNedPooling.h"
#include "EnnMemorySTLAllocator.h"

/** Come from ogre */
namespace Enn
{
	/** A set of categories that indicate the purpose of a chunk of memory
	being allocated. 
	These categories will be provided at allocation time in order to allow
	the allocation policy to vary its behaviour if it wishes. This allows you
	to use a single policy but still have variant behaviour. The level of 
	control it gives you is at a higher level than assigning different 
	policies to different classes, but is the only control you have over
	general allocations that are primitive types.
	*/
	enum MemoryCategory
	{
		/// General purpose
		MEMCATEGORY_GENERAL = 0,
		/// Geometry held in main memory
		MEMCATEGORY_GEOMETRY = 1, 
		/// Animation data like tracks, bone matrices
		MEMCATEGORY_ANIMATION = 2, 
		/// Nodes, control data
		MEMCATEGORY_SCENE_CONTROL = 3,
		/// Scene object instances
		MEMCATEGORY_SCENE_OBJECTS = 4,
		/// Other resources
		MEMCATEGORY_RESOURCE = 5,
		/// Scripting
		MEMCATEGORY_SCRIPTING = 6,
		/// Rendersystem structures
		MEMCATEGORY_RENDERSYS = 7,

		
		// sentinel value, do not use 
		MEMCATEGORY_COUNT = 8
	};

	// configure default allocators based on the options above
	// notice how we're not using the memory categories here but still roughing them out
	// in your allocators you might choose to create different policies per category

	// configurable category, for general malloc
	// notice how we ignore the category here, you could specialise

	template <MemoryCategory Cat> class _EnnExport CategorisedAllocPolicy : public NedPoolingPolicy{};
	template <MemoryCategory Cat, size_t align = 0> class _EnnExport CategorisedAlignAllocPolicy : public NedPoolingAlignedPolicy<align>{};

	// Common alloc Policy for Enn
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_GENERAL> GeneralAllocPolicy;
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_GEOMETRY> GeometryAllocPolicy;
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_ANIMATION> AnimationAllocPolicy;
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_SCENE_CONTROL> SceneCtlAllocPolicy;
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_SCENE_OBJECTS> SceneObjAllocPolicy;
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_RESOURCE> ResourceAllocPolicy;
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_SCRIPTING> ScriptingAllocPolicy;
	typedef CategorisedAllocPolicy<Enn::MEMCATEGORY_RENDERSYS> RenderSysAllocPolicy;

	// Now define all the base classes for each allocation
	template class AllocatedObject<GeneralAllocPolicy>;
	template class AllocatedObject<GeometryAllocPolicy>;
	template class AllocatedObject<AnimationAllocPolicy>;
	template class AllocatedObject<SceneCtlAllocPolicy>;
	template class AllocatedObject<SceneObjAllocPolicy>;
	template class AllocatedObject<ResourceAllocPolicy>;
	template class AllocatedObject<ScriptingAllocPolicy>;
	template class AllocatedObject<RenderSysAllocPolicy>;

	typedef AllocatedObject<GeneralAllocPolicy> GeneralAllocatedObject;
	typedef AllocatedObject<GeometryAllocPolicy> GeometryAllocatedObject;
	typedef AllocatedObject<AnimationAllocPolicy> AnimationAllocatedObject;
	typedef AllocatedObject<SceneCtlAllocPolicy> SceneCtlAllocatedObject;
	typedef AllocatedObject<SceneObjAllocPolicy> SceneObjAllocatedObject;
	typedef AllocatedObject<ResourceAllocPolicy> ResourceAllocatedObject;
	typedef AllocatedObject<ScriptingAllocPolicy> ScriptingAllocatedObject;
	typedef AllocatedObject<RenderSysAllocPolicy> RenderSysAllocatedObject;

	typedef GeneralAllocatedObject		StreamAlloc;
	typedef SceneObjAllocatedObject		MovableObjectAlloc;
	typedef SceneCtlAllocatedObject		SceneNodeAlloc;
	typedef SceneCtlAllocatedObject		SceneManagerAlloc;
	typedef RenderSysAllocatedObject	BufferAlloc;
	typedef GeneralAllocatedObject		ImageAlloc;
	typedef ResourceAllocatedObject		ResourceAlloc;
	typedef RenderSysAllocatedObject	RenderSysAlloc;
}

#if ENN_DEBUG_MODE

/// Allocate a block of raw memory, and indicate the category of usage
#	define ENN_MALLOC(bytes, category) ::Enn::CategorisedAllocPolicy<category>::allocateBytes(bytes, __FILE__, __LINE__, __FUNCTION__)
/// Allocate a block of memory for a primitive type, and indicate the category of usage
#	define ENN_ALLOC_T(T, count, category) static_cast<T*>(::Enn::CategorisedAllocPolicy<category>::allocateBytes(sizeof(T)*(count), __FILE__, __LINE__, __FUNCTION__))
/// Free the memory allocated with ENN_MALLOC or ENN_ALLOC_T. Category is required to be restated to ensure the matching policy is used
#	define ENN_FREE(ptr, category) ::Enn::CategorisedAllocPolicy<category>::deallocateBytes((void*)ptr)

/// Allocate space for one primitive type, external type or non-virtual type with constructor parameters
#	define ENN_NEW_T(T, category) new (::Enn::CategorisedAllocPolicy<category>::allocateBytes(sizeof(T), __FILE__, __LINE__, __FUNCTION__)) T
/// Allocate a block of memory for 'count' primitive types - do not use for classes that inherit from AllocatedObject
#	define ENN_NEW_ARRAY_T(T, count, category) ::Enn::constructN(static_cast<T*>(::Enn::CategorisedAllocPolicy<category>::allocateBytes(sizeof(T)*(count), __FILE__, __LINE__, __FUNCTION__)), count) 
/// Free the memory allocated with ENN_NEW_T. Category is required to be restated to ensure the matching policy is used
#	define ENN_DELETE_T(ptr, T, category) if(ptr){(ptr)->~T(); ::Enn::CategorisedAllocPolicy<category>::deallocateBytes((void*)ptr);}
/// Free the memory allocated with ENN_NEW_ARRAY_T. Category is required to be restated to ensure the matching policy is used, count and type to call destructor
#	define ENN_DELETE_ARRAY_T(ptr, T, count, category) if(ptr){for (size_t b = 0; b < count; ++b) { (ptr)[b].~T();} ::Enn::CategorisedAllocPolicy<category>::deallocateBytes((void*)ptr);}

// aligned allocation
/// Allocate a block of raw memory aligned to SIMD boundaries, and indicate the category of usage
#	define ENN_MALLOC_SIMD(bytes, category) ::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(bytes, __FILE__, __LINE__, __FUNCTION__)
/// Allocate a block of raw memory aligned to user defined boundaries, and indicate the category of usage
#	define ENN_MALLOC_ALIGN(bytes, category, align) ::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(bytes, __FILE__, __LINE__, __FUNCTION__)
/// Allocate a block of memory for a primitive type aligned to SIMD boundaries, and indicate the category of usage
#	define ENN_ALLOC_T_SIMD(T, count, category) static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(sizeof(T)*(count), __FILE__, __LINE__, __FUNCTION__))
/// Allocate a block of memory for a primitive type aligned to user defined boundaries, and indicate the category of usage
#	define ENN_ALLOC_T_ALIGN(T, count, category, align) static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(sizeof(T)*(count), __FILE__, __LINE__, __FUNCTION__))
/// Free the memory allocated with either ENN_MALLOC_SIMD or ENN_ALLOC_T_SIMD. Category is required to be restated to ensure the matching policy is used
#	define ENN_FREE_SIMD(ptr, category) ::Enn::CategorisedAlignAllocPolicy<category>::deallocateBytes(ptr)
/// Free the memory allocated with either ENN_MALLOC_ALIGN or ENN_ALLOC_T_ALIGN. Category is required to be restated to ensure the matching policy is used
#	define ENN_FREE_ALIGN(ptr, category, align) ::Enn::CategorisedAlignAllocPolicy<category, align>::deallocateBytes(ptr)

/// Allocate space for one primitive type, external type or non-virtual type aligned to SIMD boundaries
#	define ENN_NEW_T_SIMD(T, category) new (::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(sizeof(T), __FILE__, __LINE__, __FUNCTION__)) T
/// Allocate a block of memory for 'count' primitive types aligned to SIMD boundaries - do not use for classes that inherit from AllocatedObject
#	define ENN_NEW_ARRAY_T_SIMD(T, count, category) ::Enn::constructN(static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(sizeof(T)*(count), __FILE__, __LINE__, __FUNCTION__)), count) 
/// Free the memory allocated with ENN_NEW_T_SIMD. Category is required to be restated to ensure the matching policy is used
#	define ENN_DELETE_T_SIMD(ptr, T, category) if(ptr){(ptr)->~T(); ::Enn::CategorisedAlignAllocPolicy<category>::deallocateBytes(ptr);}
/// Free the memory allocated with ENN_NEW_ARRAY_T_SIMD. Category is required to be restated to ensure the matching policy is used, count and type to call destructor
#	define ENN_DELETE_ARRAY_T_SIMD(ptr, T, count, category) if(ptr){for (size_t b = 0; b < count; ++b) { (ptr)[b].~T();} ::Enn::CategorisedAlignAllocPolicy<category>::deallocateBytes(ptr);}
/// Allocate space for one primitive type, external type or non-virtual type aligned to user defined boundaries
#	define ENN_NEW_T_ALIGN(T, category, align) new (::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(sizeof(T), __FILE__, __LINE__, __FUNCTION__)) T
/// Allocate a block of memory for 'count' primitive types aligned to user defined boundaries - do not use for classes that inherit from AllocatedObject
#	define ENN_NEW_ARRAY_T_ALIGN(T, count, category, align) ::Enn::constructN(static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(sizeof(T)*(count), __FILE__, __LINE__, __FUNCTION__)), count) 
/// Free the memory allocated with ENN_NEW_T_ALIGN. Category is required to be restated to ensure the matching policy is used
#	define ENN_DELETE_T_ALIGN(ptr, T, category, align) if(ptr){(ptr)->~T(); ::Enn::CategorisedAlignAllocPolicy<category, align>::deallocateBytes(ptr);}
/// Free the memory allocated with ENN_NEW_ARRAY_T_ALIGN. Category is required to be restated to ensure the matching policy is used, count and type to call destructor
#	define ENN_DELETE_ARRAY_T_ALIGN(ptr, T, count, category, align) if(ptr){for (size_t _b = 0; _b < count; ++_b) { (ptr)[_b].~T();} ::Enn::CategorisedAlignAllocPolicy<category, align>::deallocateBytes(ptr);}

// new / delete for classes deriving from AllocatedObject (alignment determined by per-class policy)
// Also hooks up the file/line/function params
// Can only be used with classes that derive from AllocatedObject since customised new/delete needed
#	define ENN_NEW new (__FILE__, __LINE__, __FUNCTION__)
#	define ENN_DELETE delete


#else // !ENN_DEBUG_MODE

/// Allocate a block of raw memory, and indicate the category of usage
#	define ENN_MALLOC(bytes, category) ::Enn::CategorisedAllocPolicy<category>::allocateBytes(bytes)
/// Allocate a block of memory for a primitive type, and indicate the category of usage
#	define ENN_ALLOC_T(T, count, category) static_cast<T*>(::Enn::CategorisedAllocPolicy<category>::allocateBytes(sizeof(T)*(count)))
/// Free the memory allocated with ENN_MALLOC or ENN_ALLOC_T. Category is required to be restated to ensure the matching policy is used
#	define ENN_FREE(ptr, category) ::Enn::CategorisedAllocPolicy<category>::deallocateBytes((void*)ptr)

/// Allocate space for one primitive type, external type or non-virtual type with constructor parameters
#	define ENN_NEW_T(T, category) new (::Enn::CategorisedAllocPolicy<category>::allocateBytes(sizeof(T))) T
/// Allocate a block of memory for 'count' primitive types - do not use for classes that inherit from AllocatedObject
#	define ENN_NEW_ARRAY_T(T, count, category) ::Enn::constructN(static_cast<T*>(::Enn::CategorisedAllocPolicy<category>::allocateBytes(sizeof(T)*(count))), count) 
/// Free the memory allocated with ENN_NEW_T. Category is required to be restated to ensure the matching policy is used
#	define ENN_DELETE_T(ptr, T, category) if(ptr){(ptr)->~T(); ::Enn::CategorisedAllocPolicy<category>::deallocateBytes((void*)ptr);}
/// Free the memory allocated with ENN_NEW_ARRAY_T. Category is required to be restated to ensure the matching policy is used, count and type to call destructor
#	define ENN_DELETE_ARRAY_T(ptr, T, count, category) if(ptr){for (size_t b = 0; b < count; ++b) { (ptr)[b].~T();} ::Enn::CategorisedAllocPolicy<category>::deallocateBytes((void*)ptr);}

// aligned allocation
/// Allocate a block of raw memory aligned to SIMD boundaries, and indicate the category of usage
#	define ENN_MALLOC_SIMD(bytes, category) ::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(bytes)
/// Allocate a block of raw memory aligned to user defined boundaries, and indicate the category of usage
#	define ENN_MALLOC_ALIGN(bytes, category, align) ::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(bytes)
/// Allocate a block of memory for a primitive type aligned to SIMD boundaries, and indicate the category of usage
#	define ENN_ALLOC_T_SIMD(T, count, category) static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(sizeof(T)*(count)))
/// Allocate a block of memory for a primitive type aligned to user defined boundaries, and indicate the category of usage
#	define ENN_ALLOC_T_ALIGN(T, count, category, align) static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(sizeof(T)*(count)))
/// Free the memory allocated with either ENN_MALLOC_SIMD or ENN_ALLOC_T_SIMD. Category is required to be restated to ensure the matching policy is used
#	define ENN_FREE_SIMD(ptr, category) ::Enn::CategorisedAlignAllocPolicy<category>::deallocateBytes((void*)ptr)
/// Free the memory allocated with either ENN_MALLOC_ALIGN or ENN_ALLOC_T_ALIGN. Category is required to be restated to ensure the matching policy is used
#	define ENN_FREE_ALIGN(ptr, category, align) ::Enn::CategorisedAlignAllocPolicy<category, align>::deallocateBytes((void*)ptr)

/// Allocate space for one primitive type, external type or non-virtual type aligned to SIMD boundaries
#	define ENN_NEW_T_SIMD(T, category) new (::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(sizeof(T))) T
/// Allocate a block of memory for 'count' primitive types aligned to SIMD boundaries - do not use for classes that inherit from AllocatedObject
#	define ENN_NEW_ARRAY_T_SIMD(T, count, category) ::Enn::constructN(static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category>::allocateBytes(sizeof(T)*(count))), count) 
/// Free the memory allocated with ENN_NEW_T_SIMD. Category is required to be restated to ensure the matching policy is used
#	define ENN_DELETE_T_SIMD(ptr, T, category) if(ptr){(ptr)->~T(); ::Enn::CategorisedAlignAllocPolicy<category>::deallocateBytes((void*)ptr);}
/// Free the memory allocated with ENN_NEW_ARRAY_T_SIMD. Category is required to be restated to ensure the matching policy is used, count and type to call destructor
#	define ENN_DELETE_ARRAY_T_SIMD(ptr, T, count, category) if(ptr){for (size_t b = 0; b < count; ++b) { (ptr)[b].~T();} ::Enn::CategorisedAlignAllocPolicy<category>::deallocateBytes((void*)ptr);}
/// Allocate space for one primitive type, external type or non-virtual type aligned to user defined boundaries
#	define ENN_NEW_T_ALIGN(T, category, align) new (::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(sizeof(T))) T
/// Allocate a block of memory for 'count' primitive types aligned to user defined boundaries - do not use for classes that inherit from AllocatedObject
#	define ENN_NEW_ARRAY_T_ALIGN(T, count, category, align) ::Enn::constructN(static_cast<T*>(::Enn::CategorisedAlignAllocPolicy<category, align>::allocateBytes(sizeof(T)*(count))), count) 
/// Free the memory allocated with ENN_NEW_T_ALIGN. Category is required to be restated to ensure the matching policy is used
#	define ENN_DELETE_T_ALIGN(ptr, T, category, align) if(ptr){(ptr)->~T(); ::Enn::CategorisedAlignAllocPolicy<category, align>::deallocateBytes((void*)ptr);}
/// Free the memory allocated with ENN_NEW_ARRAY_T_ALIGN. Category is required to be restated to ensure the matching policy is used, count and type to call destructor
#	define ENN_DELETE_ARRAY_T_ALIGN(ptr, T, count, category, align) if(ptr){for (size_t _b = 0; _b < count; ++_b) { (ptr)[_b].~T();} ::Enn::CategorisedAlignAllocPolicy<category, align>::deallocateBytes((void*)ptr);}

// new / delete for classes deriving from AllocatedObject (alignment determined by per-class policy)
#	define ENN_NEW new 
#	define ENN_DELETE delete

#endif // ENN_DEBUG_MODE

namespace Enn
{
	/** Utility function for constructing an array of objects with placement new,
		without using new[] (which allocates an undocumented amount of extra memory
		and so isn't appropriate for custom allocators).
	*/
	template<typename T>
	T* constructN(T* basePtr, size_t count)
	{
		for (size_t i = 0; i < count; ++i)
		{
			new ((void*)(basePtr+i)) T();
		}
		return basePtr;
	}

	template <typename T>
	void safe_delete(T*& ptr)
	{
		ENN_DELETE ptr;
		ptr = 0;
	}
}

#endif    /* EnnMemoryAllocatorConfig_h__ */